module Test.Spago.Bundle where

import Test.Prelude

import Data.String as Str
import Spago.Command.Bundle (checkWatermarkMarkerFileName)
import Spago.FS as FS
import Spago.Generated.BuildInfo as BuildInfo
import Test.Spec (Spec)
import Test.Spec as Spec
import Test.Spec.Assertions (shouldNotEqual)
import Test.Spec.Assertions.String (shouldStartWith)

spec :: Spec Unit
spec = Spec.around withTempDir do
  Spec.describe "bundle" do

    Spec.it "bundles into an app (browser)" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "bundle", "-v", "--bundle-type", "app", "--outfile", "bundle-app-browser.js" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "bundle-app-browser.js") (fixture "bundle-app-browser.js")

    Spec.it "bundles into an app (node)" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "bundle", "-v", "--bundle-type", "app", "--outfile", "bundle-app-node.js", "--platform", "node" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "bundle-app-node.js") (fixture "bundle-app-node.js")

    Spec.it "bundles into a module" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "build" ] >>= shouldBeSuccess
      spago [ "bundle", "--bundle-type=module", "--outfile", "bundle-module.js" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "bundle-module.js") (fixture "bundle-module.js")

    Spec.it "bundles an app with source map (browser)" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "bundle", "-v", "--outfile", "bundle-app-browser-map.js", "--source-maps", "--bundle-type", "app" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "bundle-app-browser-map.js") (fixture "bundle-app-browser-map.js")
      checkFixture (testCwd </> "bundle-app-browser-map.js.map") (fixture "bundle-app-browser-map.js.map")

    Spec.it "bundles an app with source map (node)" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "bundle", "-v", "--outfile", "bundle-app-node-map.js", "--source-maps", "--bundle-type", "app", "--platform", "node" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "bundle-app-node-map.js") (fixture "bundle-app-node-map.js")
      checkFixture (testCwd </> "bundle-app-node-map.js.map") (fixture "bundle-app-node-map.js.map")

    Spec.it "bundles a module with source map" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "build" ] >>= shouldBeSuccess
      spago [ "bundle", "--bundle-type", "module", "--outfile", "bundle-module-map.js", "--source-maps" ] >>= shouldBeSuccess

      checkBundle (testCwd </> "bundle-module-map.js") (fixture "bundle-module-map.js")
      checkFixture (testCwd </> "bundle-module-map.js.map") (fixture "bundle-module-map.js.map")

    Spec.it "bundles a module with extra esbuild arguments" \{ spago, fixture, testCwd } -> do
      spago [ "init" ] >>= shouldBeSuccess
      spago [ "build" ] >>= shouldBeSuccess
      spago [ "bundle", "--bundle-type", "module", "--outfile", "bundle-module-map.js", "--source-maps" ] >>= shouldBeSuccess

      checkBundle (testCwd </> "bundle-module-map.js") (fixture "bundle-module-map.js")
      checkFixture (testCwd </> "bundle-module-map.js.map") (fixture "bundle-module-map.js.map")

    Spec.it "refuses to overwrite existing bundle that is not Spago-generated" \{ spago, fixture, testCwd } -> do
      FS.writeTextFile (testCwd </> checkWatermarkMarkerFileName) "."
      spago [ "init", "--name", "project" ] >>= shouldBeSuccess
      spago [ "bundle" ] >>= shouldBeSuccess
      spago [ "bundle" ] >>= shouldBeSuccess
      FS.readTextFile (testCwd </> "index.js") >>= \content -> content `shouldStartWith` "/* Generated by Spago"
      FS.writeTextFile (testCwd </> "index.js") "Bogus"
      spago [ "bundle" ] >>= shouldBeFailureErr (fixture "bundle-refuse-overwrite-output.txt")
      FS.readTextFile (testCwd </> "index.js") >>= shouldEqual "Bogus"
      spago [ "bundle", "--force" ] >>= shouldBeSuccess
      FS.readTextFile (testCwd </> "index.js") >>= shouldNotEqual "Bogus"

    Spec.it "overwrites non-Spago-generated bundle when there is no magic marker file" \{ spago, fixture, testCwd } -> do
      spago [ "init", "--name", "project" ] >>= shouldBeSuccess
      spago [ "bundle" ] >>= shouldBeSuccess
      FS.writeTextFile (testCwd </> "index.js") "Bogus"
      spago [ "bundle" ] >>= shouldBeSuccess
      checkBundle (testCwd </> "index.js") (fixture "bundle-default.js")

  where
  -- This is a version of `checkFixture`, but it replaces the "v0" placeholder
  -- in the bundle magic marker with the actual current build version. Fixture
  -- files have the "v0" placeholder in them because we can't put the current
  -- build version there, as it changes with every build.
  checkBundle file fixture =
    checkFixture' file fixture \actual expected ->
      Str.trim actual `shouldEqualStr` patchWatermark (Str.trim expected)
    where
    watermark version = "/* Generated by Spago v" <> version

    patchWatermark =
      Str.replace
        (Str.Pattern $ watermark "0")
        (Str.Replacement $ watermark BuildInfo.packages."spago-bin")
